package com.gegong.vmware.workstation.utils;


import com.gegong.vmware.workstation.exceptions.VmxFileException;
import com.google.common.collect.Maps;
import javafx.util.Pair;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 提供直接修改vm虚拟机设置文件(.vmx)的相关方法
 *
 * <p>
 * 这个类只是用于在官方rest api功能尚未完善之前过渡一下
 *
 * @author dongjak
 * @version 1.0
 * @since 1.0
 */
public class VmxFileUtils {

    private File vmxFile;
    private Map<Integer, Pair<String, String>> settings = Maps.newConcurrentMap();

    public VmxFileUtils(String vmxFilePath) throws VmxFileException {
        this.vmxFile = new File(vmxFilePath);
        createBakFile();
        readSettings();

    }

    private void readSettings() throws VmxFileException {
        try {
            List<String> lines = FileUtils.readLines(vmxFile, getFileCharset());
            for (int i = 1; i <= lines.size(); i++) {
                String line = lines.get(i - 1);
                settings.put(i, new Pair<>(readKey(line), readValue(line)));
            }
        } catch (IOException e) {
            throw VmxFileException.newReadSettingsException(vmxFile, e);
        }
    }

    private void createBakFile() throws VmxFileException {
        String oldFilePath = vmxFile.getAbsolutePath();
        String bakFilePath = oldFilePath.substring(0, oldFilePath.lastIndexOf(".")) + "_bak" + oldFilePath.substring(oldFilePath.lastIndexOf("."));
        File bakFile = new File(bakFilePath);
        if (bakFile.exists()) return;

        try {
            bakFile.createNewFile();
            FileUtils.copyFile(vmxFile, bakFile);
        } catch (IOException e) {
            throw VmxFileException.newCreateBakException(vmxFile, e);
        }
    }


    public void setMacAddress(int slot, String newMacAddress) throws VmxFileException {
        addSetting(String.format("ethernet%d.address", slot), newMacAddress);
    }

    private Charset getFileCharset() {
        try {
            String charsetSettingLine = FileUtils.readLines(vmxFile, StandardCharsets.UTF_8).stream().filter(line -> {
                return line.trim().startsWith(".encoding");
            }).findFirst().get();
            return Charset.forName(readValue(charsetSettingLine));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return StandardCharsets.UTF_8;
    }

    public int existSetting(String settingName) {
        List<Map.Entry<Integer, Pair<String, String>>> existedSetting = settings.entrySet().stream().filter(pair -> {
            return pair.getValue().getKey().equals(settingName);
        }).collect(Collectors.toList());

        return existedSetting.isEmpty() ? -1 : existedSetting.get(0).getKey();

    }


    /**
     * 添加设置,如果存在就替换
     *
     * @param key
     * @param value
     */
    public synchronized void addSetting(String key, String value) throws VmxFileException {
        int existIndex = existSetting(key);
        int settingIndex = settings.size();
        if (existIndex != -1) settingIndex = existIndex;
        settings.put(settingIndex, new Pair<>(key, value));
        try {
            FileUtils.writeLines(vmxFile, getFileCharset().name(), getSettingLines());
        } catch (IOException e) {
            throw VmxFileException.newAddSettingsException(this.vmxFile, e);
        }
    }

    public String getSettingVlaue(String settingKey) {
        try {
            String settingLine = FileUtils.readLines(vmxFile, getFileCharset()).stream().filter(line -> {
                return line.trim().startsWith(settingKey);
            }).findFirst().get();

            return readValue(settingLine);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String readKey(String line) {
        return line.substring(0, line.indexOf("=")).trim();
    }

    private String readValue(String line) {
        return line.substring(line.indexOf("\"") + 1, line.lastIndexOf("\"")).trim();
    }

    private List<String> getSettingLines() {
        List<String> settingLines = new ArrayList<>(settings.size());
        settings.forEach((lineNum, setting) -> {
            settingLines.add(lineNum - 1, String.format("%s = \"%s\"", setting.getKey(), setting.getValue()));
        });

        return settingLines;
    }


    public String getDsplayName() {
        return getSettingVlaue("displayName");
    }

}
